﻿using System;
using System.Diagnostics;

using Microsoft.Xna.Framework;

using SmlEngine.Sprites.Base;
using SmlEngine.Sprites.Collections;

namespace SmlEngine.Sprites.Components
{
    public class CollisionComponent : SpriteComponent
    {
        public bool IsOnGround { get; set; }

        public bool HandleMove { get; set; }

        public Action<Direction> OnCollide { get; set; }

        public CollisionComponent(Npc sprite)
            : base(sprite)
        {
            HandleMove = true;
        }

        public override void Update(GameTime gameTime)
        {
            Npc owner = (Npc)Owner;

            IsOnGround = false;
            Vector2 depth;

            for (int i = 0; i < owner.Owner.Layers.Count; i++)
            {
                var tiles = owner.Owner.Layers[i].StaticSprites;

                for (int j = 0; j < tiles.ActiveCount; j++)
                {
                    var sprite = tiles.GetActiveItem(j);

                    if (sprite is Tile)
                    {
                        var tile = (Tile)sprite;

                        depth = owner.ProjectedBounds.GetIntersectionDepth(tile.Bounds);

                        if (depth != Vector2.Zero)
                        {
                            Direction collisionDirection;

                            float absX = Math.Abs(depth.X);
                            float absY = Math.Abs(depth.Y);

                            if (absX < absY) // The collision is deeper on the side of the tile.
                            {
                                collisionDirection = depth.X < 0 ? Direction.Left : Direction.Right;
                            }
                            else // The collision is deeper from the top or bottom.
                            {
                                collisionDirection = depth.Y < 0 ? Direction.Top : Direction.Bottom;
                            }

                            bool isSlope = tile.Collision.Gradient.Slope != 0;

                            if (HandleMove)
                            {
                                if (isSlope)
                                {
                                    #region Sloped Collision

                                    Vector2 angle, up, across;

                                    if (tile.Collision.Inverted)
                                    {
                                        float left = tile.Collision.Bounds.Left;
                                        float right = tile.Collision.Bounds.Right;
                                        float top = tile.Collision.Bounds.Top;
                                        float bottom = tile.Collision.Bounds.Bottom;

                                        bool upwards = tile.Collision.Gradient.Rise > 0;

                                        angle = new Vector2(upwards ? left : right, top);
                                        up = new Vector2(left, upwards ? bottom : top);
                                        across = new Vector2(right, upwards ? top : bottom);
                                    }
                                    else
                                    {
                                        float left = tile.Collision.Bounds.Left;
                                        float right = tile.Collision.Bounds.Right;
                                        float top = tile.Collision.Bounds.Top;
                                        float bottom = tile.Collision.Bounds.Bottom;

                                        bool upwards = tile.Collision.Gradient.Rise > 0;

                                        angle = new Vector2(upwards ? right : left, top);
                                        up = new Vector2(left, upwards ? bottom : top);
                                        across = new Vector2(right, upwards ? top : bottom);
                                    }

                                    RightTriangle triangle = new RightTriangle(angle, across, up);
                                    
                                    //if(!tile.Slope.Has(Collision.Passable))
                                        owner.ProjectedPosition = new Vector2(owner.ProjectedPosition.X,
                                                                 owner.ProjectedPosition.Y +
                                                                 triangle.GetIntersectionDepth(owner.ProjectedBounds));

                                    #endregion

                                    Collide(collisionDirection);
                                }
                                else
                                {
                                    #region Tile Collision

                                    switch (collisionDirection)
                                    {
                                        case Direction.Left:
                                            //if (!tile.Left.Has(Collision.Passable))
                                                ResolveCollisionX(depth.X);

                                            break;

                                        case Direction.Right:
                                            //if (!tile.Right.Has(Collision.Passable))
                                                ResolveCollisionX(depth.X);

                                            break;

                                        case Direction.Top:
                                            //if (!tile.Top.Has(Collision.Passable))
                                            //{
                                                ResolveCollisionY(depth.Y);
                                                IsOnGround = true;
                                            //}

                                            break;

                                        case Direction.Bottom:
                                            //if (!tile.Bottom.Has(Collision.Passable))
                                                ResolveCollisionY(depth.Y);

                                            break;
                                    }

                                    #endregion

                                    Collide(collisionDirection);
                                }
                            }

                            #region Old (0.03b)

                            /* bool left = depth.X >= 0;
                            bool top = depth.Y >= 0;
                            
                            Vector2 change = Vector2.Zero;

                            if (left && tile.Left != Collision.Impassable)
                            {
                                change.X -= depth.X;
                                owner.Velocity = Vector2.Zero;
                                owner.Acceleration = Vector2.Zero;

                                Collide(Direction.Left);
                            }
                            else if (!left && tile.Right != Collision.Impassable)
                            {
                                change.X -= depth.X;
                                owner.Velocity = Vector2.Zero;
                                owner.Acceleration = Vector2.Zero;

                                Collide(Direction.Right);
                            }

                            if (top && tile.Top != Collision.Impassable)
                            {
                                change.Y -= depth.Y;
                                owner.Velocity = Vector2.Zero;
                                owner.Acceleration = Vector2.Zero;

                                IsOnGround = true;

                                Collide(Direction.Top);
                            }
                            else if (!top && tile.Bottom != Collision.Impassable)
                            {
                                change.Y -= depth.Y;
                                owner.Velocity = Vector2.Zero;
                                owner.Acceleration = Vector2.Zero;

                                Collide(Direction.Bottom);
                            }

                            Vector2 movement = owner.Position - owner.PreviousPosition;

                            if (movement.X > movement.Y)
                            {
                                owner.Position = new Vector2(owner.Position.X - change.X, owner.Position.Y);
                            }
                            else if (movement.Y > movement.X)
                            {
                                owner.Position = new Vector2(owner.Position.X, owner.Position.Y - change.Y);
                            }
                            else
                            {
                                owner.Position -= change;
                            } */

                            #endregion
                        }
                    }
                }
            }
        }

        private void ResolveCollisionX(float depth)
        {
            Owner.ProjectedPosition = new Vector2(Owner.ProjectedPosition.X + depth, Owner.ProjectedPosition.Y);
            Owner.Velocity = new Vector2(0, Owner.Velocity.Y);
            Owner.Acceleration = new Vector2(0, Owner.Acceleration.Y);
        }

        private void ResolveCollisionY(float depth)
        {
            Owner.ProjectedPosition = new Vector2(Owner.ProjectedPosition.X, Owner.ProjectedPosition.Y + depth);
            Owner.Velocity = new Vector2(Owner.Velocity.X, 0);
            Owner.Acceleration = new Vector2(Owner.Acceleration.X, 0);
        }

        private void Collide(Direction direction)
        {
            if (OnCollide != null)
                OnCollide(direction);
        }
    }
}
